package net.vinote.smart.platform.module.dbpool;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * java.sql.Connection的代理对象
 * 
 * @author Seer
 *
 */
class ProxyConnection implements InvocationHandler {

	/**
	 * 真实的JDBC对象
	 */
	private Connection conn = null;

	private Connection proxyConn = null;
	// 用户最后一次访问该连接方法的时间
	private AtomicLong lastAccessTime = new AtomicLong(
			System.currentTimeMillis());
	/**
	 * Connection修改标志,每一次回收都被刷新数值
	 */
	private AtomicInteger modifyTag = new AtomicInteger();

	/**
	 * Connectin对象是否被引用
	 */
	private AtomicBoolean referFlag = new AtomicBoolean(false);

	public ProxyConnection(Connection conn) throws Exception {
		this.conn = conn;
		proxyConn = (Connection) Proxy.newProxyInstance(conn.getClass()
				.getClassLoader(), new Class[] { Connection.class }, this);
	}

	/**
	 * 该方法真正的关闭了数据库的连接
	 * 
	 * @throws SQLException
	 */
	public synchronized void close() {
		try {
			modifyTag.incrementAndGet();// 与原Connection关联的对象设置失效标志
			conn.close();
			referFlag.set(true);// 由连接池占用当前ProxyConnection
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取但不引用Connection对象
	 * 
	 * @return
	 */
	public Connection getConnectionButUnrefer() {
		return getConnection(false);
	}

	/**
	 * Returns the lastAccessTime.
	 * 
	 * @return long
	 */
	public final long getLastAccessTime() {

		return lastAccessTime.get();
	}

	public int getModifyFlag() {
		return modifyTag.get();
	}

	/**
	 * 当前Connection是否已被引用
	 * 
	 * @return
	 */
	public boolean hasReference() {
		return referFlag.get();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
	 * java.lang.reflect.Method, java.lang.Object[])
	 */
	public Object invoke(Object proxy, Method m, Object[] args)
			throws Throwable {
		if (!Thread.currentThread().getName()
				.equals(DBConnectionPool.POOL_RECYCLE_THREAD_NAME)) {
			if (!referFlag.get()) {
				throw new NullPointerException(
						"Connection is not initialized,could not call method "
								+ m);
			}
		}
		Object obj = null;

		if ("close".equals(m.getName())) {
			modifyTag.getAndIncrement();
			conn.clearWarnings();
			referFlag.set(false);// 释放对于Connection的引用
		} else if ("prepareStatement".equals(m.getName())) {
			ProxyPreparedStatement ps = new ProxyPreparedStatement(this,
					(PreparedStatement) m.invoke(conn, args));
			obj = ps.preparedStatement();
		} else {
			obj = m.invoke(conn, args);
		}
		// 设置最后一次访问时间，以便及时清除超时的连接
		updateAccessTime();
		return obj;
	}

	/**
	 * 引用Connection对象
	 * 
	 * @return
	 */
	public synchronized Connection refereConnection() {
		return getConnection(true);
	}

	public final void updateAccessTime() {
		lastAccessTime.set(System.currentTimeMillis());
	}

	/**
	 * @param referFlag
	 *            设置是否引用标志
	 * @return
	 */
	private Connection getConnection(boolean referFlag) {
		if (referFlag) {
			// 当前Connection若已被引用,则不可被重复引用
			if (this.referFlag.get()) {
				throw new RuntimeException(Thread.currentThread()
						+ "[Current Connection has already referenced! ");
			}
			this.referFlag.set(true);
			modifyTag.getAndIncrement();
			updateAccessTime();// 被引用之初需要刷新时间,否则有极高的概率被回收线程回收资源
		}
		return proxyConn;
	}
}
